// #![feature(string_remove_matches)]
use regex::Regex;
use std::borrow;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;

// pub struct HandleProto {
//     pub fn new() -> Self {
//         HandleProto()
//     }
// }

struct HandleProto {
  reader: BufReader<File>,
}
impl HandleProto {
  pub fn new() -> Self {
    let file_ret = File::open("input/IM.proto");
    match file_ret {
      Ok(file) => {
        let reader = std::io::BufReader::new(file);
        return HandleProto { reader };
      }
      Err(e) => panic!("{:?}", e),
    }
  }
  pub fn proto_to_cpp(&mut self) {
    let mut output = Vec::<String>::new();
    loop {
      let mut content = String::new();
      let count = self.read_one_line(&mut content);
      if 0 == count {
        break; // EOF
      }
      if content.trim().is_empty() {
        continue; // empty line
      }
      if content.contains("package") {
        output.push(self.handle_package(content));
      } else if content.contains("message") {
        output.push(self.handle_message(content));
      } else if content.contains("enum") {
        output.push(self.handle_enum(content));
      }
    }
  }
  fn read_one_line(&mut self, buf: &mut String) -> usize {
    let read_ret = self.reader.read_line(buf);
    match read_ret {
      Ok(count) => {
        return count;
      }
      Err(e) => {
        panic!("{}", e);
      }
    }
  }
  fn handle_package(&mut self, content: String) -> String {
    let c = content.replace("package", "");
    let cc = c.replace(";", "");
    let list = cc.split('.');
    let mut output = String::new();
    for elem in list {
      if !elem.is_empty() {
        output.push_str("namespace ");
        output.push_str(elem);
        output.push_str(" { ");
      }
    }
    return content;
  }
  fn handle_enum(&mut self, content: String) -> String {
    fn _handle_enum_oneline(l: &mut u8, output: &mut String, content: String) {
      for c in content.chars() {
        if ';' == c {
          output.push(',');
        } else {
          output.push(c);
          if '{' == c {
            *l += 1;
          } else if '}' == c {
            *l -= 1;
            if 0 == *l {
              output.push_str(";");
            }
          }
        }
      }
    }
    let mut l = 0u8; // {
    let mut output = String::new();
    _handle_enum_oneline(&mut l, &mut output, content);
    while 0 < l {
      let mut _content = String::new();
      let ret = self.read_one_line(&mut _content);
      if 0 < ret {
        _handle_enum_oneline(&mut l, &mut output, _content);
      }
    }
    println!("output:{}", output);
    return output;
  }
  fn handle_message(&mut self, content: String) -> String {
    fn _handle_message_oneline(l: &mut u8, output: &mut String, content: String) {
      let rrr = content.replace("message", "struct");
      let regex = Regex::new(r"=.+[0-9];").expect("");
      let r = regex.replace(rrr.as_str(), ";").to_string();
      // let r = content.replace(r"=.+[0-9];", ";");
      let rr: String;
      if r.contains("string") {
        rr = r.replace("string", "std::string");
      } else if r.contains("int32") {
        rr = r.replace("int32", "std::int32_t");
      } else if r.contains("int64") {
        rr = r.replace("int64", "std::int64_t");
      } else if r.contains("oneof") {
        rr = r.replace("oneof", "union");
      } else if r.contains("map") {
        rr = r.replace("map", "std::map");
      } else if r.contains("repeated") {
        //         let re = Regex::new(r"[a-z]+(?:([0-9]+)|([A-Z]+))").unwrap();
        // let caps = re.captures("abc123").unwrap();

        // let text1 = caps.get(1).map_or("", |m| m.as_str());
        // let text2 = caps.get(2).map_or("", |m| m.as_str());
        // assert_eq!(text1, "123");
        // assert_eq!(text2, "");

        // let re = Regex::new(r"'([^']+)'\s+\((\d{4})\)").unwrap();
        // let text = "Not my favorite movie: 'Citizen Kane' (1941).";
        // let caps = re.captures(text).unwrap();
        // assert_eq!(caps.get(1).unwrap().as_str(), "Citizen Kane");
        // assert_eq!(caps.get(2).unwrap().as_str(), "1941");
        // assert_eq!(caps.get(0).unwrap().as_str(), "'Citizen Kane' (1941)");

        // let re = Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)").unwrap();
        // let text = "Not my favorite movie: 'Citizen Kane' (1941).";
        // let caps = re.captures(text).unwrap();
        // assert_eq!(caps.name("title").unwrap().as_str(), "Citizen Kane");
        // assert_eq!(caps.name("year").unwrap().as_str(), "1941");
        // assert_eq!(caps.get(0).unwrap().as_str(), "'Citizen Kane' (1941)");

        // let regex = Regex::new(r"repeated\s[a-z|A-Z|0-9]+").expect("");
        // let keyword = regex.captures(r.as_str());
        // match keyword {
        //   Some(caps) => {
        //     let sss = caps.name("keyword").expect("msg").as_str();
        //     println!("{}", sss);
        //     // let s = caps.get(1).expect("1").as_str();
        //     // let text1 = caps.get(1).map_or("", |m| {
        //     //   println!("{}", m.as_str());
        //     //   m.as_str()
        //     // });
        //     // let keyword = String::from_str(text1)
        //     //   .expect("msg")
        //     //   .replace("repeated ", "");
        //     // println!("{}, {}", keyword, text1);
        //   }
        //   None => {}
        // }
        // let ret = regex.shortest_match(r.as_str()).expect("msg");
        // let rrrr = regex.replace(r.as_str(), ";").to_string();
        // rr = r.replace("repeated", "std::vector<");

        rr = r;
      } else {
        rr = r;
      }
      for c in rr.chars() {
        output.push(c);
        if '{' == c {
          *l += 1;
        } else if '}' == c {
          *l -= 1;
          if 0 == *l {
            output.push_str(";");
          }
        }
      }
    }
    let mut l = 0u8; // {
    let mut output = String::new();
    _handle_message_oneline(&mut l, &mut output, content);
    while 0 < l {
      let mut _content = String::new();
      let ret = self.read_one_line(&mut _content);
      if 0 < ret {
        _handle_message_oneline(&mut l, &mut output, _content);
      }
    }
    println!("output:{}", output);
    return output;
  }
}

fn main() {
  // read_proto_file();
  let mut obj = HandleProto::new();
  obj.proto_to_cpp();
}
